/*************************************************************************
 *                                                                       *
 * This file is part of the FreeMAPs distribution.  For full copyright   *
 * information, see COPYRIGHT and COPYING.LESSER.                        *
 *                                                                       *
 * Author:           sqbang                                              *
 * Email:            sqbang@ustc.edu                                     *
 * Date:             2011.08.22                                          *
 *                                                                       *
 *************************************************************************/

//declaration header
#include "COMMON/bdry/IrregularBoundaryConditionStrategy.h"

//COMMON headers
#include "COMMON/bdry/DirichletBoundaryCondition.h"
#include "COMMON/bdry/HeatSourceBoundaryCondition.h"
#include "COMMON/bdry/NeumannBoundaryCondition.h"
#include "COMMON/bdry/RobinBoundaryCondition.h"
#include "COMMON/bdry/PressureBoundaryCondition.h"
#include "COMMON/bdry/DistributedLoadBoundaryCondition.h"
#include "COMMON/bdry/ThermalStressLoadBoundaryCondition.h"
#include "COMMON/equa/PDEEquationEnsembleStrategy.h"
#include "COMMON/equa/EquationDomain.h"
#include "COMMON/info/InfoManager.h"
#include "COMMON/info/CommonInfo.h"
#include "COMMON/tbox/Utilities.h"
#include "COMMON/tbox/LogMessage.h"



namespace FreeMAPs
{

IrregularBoundaryConditionStrategy::IrregularBoundaryConditionStrategy (
        const std::string& parent_name,
        const std::string& bc_name,
        const PDEEquationEnsembleStrategy* equation_ensemble,
        bool is_registered_in_info_manager)
    : Parent (parent_name, bc_name,is_registered_in_info_manager),
    d_bc_name (bc_name),
    d_act_range (this->get_full_name(), "act_range", equation_ensemble->use_structured_mesh(),is_registered_in_info_manager),
    d_equation_ensemble (equation_ensemble)
{
}



IrregularBoundaryConditionStrategy::~IrregularBoundaryConditionStrategy()
{
    this->clear();
}



void
IrregularBoundaryConditionStrategy::clear()
{
    d_bc_name.clear();
}



IrregularBoundaryConditionStrategy*
IrregularBoundaryConditionStrategy::create_object (
    const BoundaryConditionType bc_type,
    const std::string& parent_name,
    const std::string& bc_name,
    const PDEEquationEnsembleStrategy* equation_ensemble)
{
    IrregularBoundaryConditionStrategy* bc = 0;
    switch (bc_type)
    {
    case BoundaryConditionType::DIRICHLET:
        {
            bc = new DirichletBoundaryCondition (parent_name, bc_name, equation_ensemble);
            break;
        }
    case BoundaryConditionType::HEAT_SOURCE:
        {
            bc = new HeatSourceBoundaryCondition (parent_name, bc_name, equation_ensemble);
            break;
        }
    case BoundaryConditionType::NEUMANN:
        {
            bc = new NeumannBoundaryCondition (parent_name, bc_name, equation_ensemble);
            break;
        }
    case BoundaryConditionType::ROBIN:
        {
            bc = new RobinBoundaryCondition (parent_name, bc_name, equation_ensemble);
            break;
        }
    case BoundaryConditionType::PRESSURE:
        {
            bc = new PressureBoundaryCondition (parent_name, bc_name, equation_ensemble);
            break;
        }
    case BoundaryConditionType::DISTRIBUTED_LOAD:
        {
            bc = new DistributedLoadBoundaryCondition (parent_name, bc_name, equation_ensemble);
            break;
        }
    case BoundaryConditionType::THERMAL_STRESS:
        {
            bc = new ThermalStressLoadBoundaryCondition (parent_name, bc_name, equation_ensemble);
            break;
        }
    default:
        {
            FREEMAPS_FIND_BUG();
        }
    }
    return bc;
}



void
IrregularBoundaryConditionStrategy::get_from_input (
    const InputDatabase* db)
{
    this->prepare_for_use (db);

    db->insert_allowable_key ("bc_type");
    db->insert_allowable_key ("act_range");
#ifdef FREEMAPS_HAVE_SAMRAI
    db->insert_allowable_key ("act_type");
    db->insert_allowable_key ("discrete_parameters");
#endif
    db->insert_allowable_key ("physical_parameters");

    if (this->require_set_variable())
    {
        db->insert_allowable_key ("variable_symbol");
    }

    db->check_allowable_keys();

    /*
     * the boundary_condition_type will be read before the boundary condition is build.
     * so do NOT read it here.
     */
    //const bool act_on_structured_mesh = d_equation_ensemble->get_equation_domain()->get_region()->use_structured_mesh();

    const bool act_on_structured_mesh = false;//unstructured grid
#if 0
    /*
     * build variable_symbol
     */
    if (this->require_set_variable())
    {
        const VariableInfo* var_info = InfoManager::get_instance()->get_variable_info();

        db->check_requisite_key ("variable_symbol", DataType::STRING, false, false);
        d_variable_complete_symbol = db->get_string ("variable_symbol");
        if (!var_info->has_variable_by_complete_symbol (d_variable_complete_symbol))
        {
            LogMessage::use_improper_string_value (
                db,
                "variable_symbol",
                "it should be a complete symbol set in \"base_information --> variable_info\"",
                var_info->get_variable_complete_symbols());
        }

        if (!d_equation_ensemble->has_variable_by_complete_symbol (d_variable_complete_symbol))
        {
            LogMessage::use_improper_string_value (
                db,
                "variable_symbol",
                d_variable_complete_symbol,
                "\"" + d_variable_complete_symbol + "\" is not found as a variable complete symbol in \"EquationEnsemble\" of \"" + d_equation_ensemble->get_equation_ensemble_name() + "\"",
                d_equation_ensemble->get_all_main_variable_complete_symbols());
        }
    }
#endif
    /*
     * build act_range
     */
    {
        if (act_on_structured_mesh)
        {
            db->check_requisite_database ("act_range");
        }
        else
        {
            db->check_requisite_key ("act_range", DataType::DATABASE, false, false);
        }

        if(db->key_exists("act_range") && db->is_database("act_range"))
        {
            d_act_range.get_from_input (db->get_database ("act_range"));
        }
    }

    /*
     * build physical_parameters
     */
    {
        if(db->key_exists("physical_parameters") && db->is_database("physical_parameters"))
        {
            db->check_requisite_key ("physical_parameters", DataType::DATABASE, false, false);
            this->get_info_for_derived_object_from_input (db->get_database ("physical_parameters"));
        }
    }
}



std::ostream&
operator << (
    std::ostream& os,
    const IrregularBoundaryConditionStrategy& rhs)
{
//    os << "boundary_condition_type = \"" << Utilities::enum_to_string (rhs.get_type()) << "\"\n";
    os << "bc_type = \"" << Utilities::enum_to_string (rhs.get_type()) << "\"\n";

    if (rhs.require_set_variable())
    {
//        os << "variable_symbol         = \"" << rhs.d_variable_complete_symbol << "\"\n";
        os << "variable_symbol  = \"" << "T" << "\"\n";
    }
    /*
     * output boundary_condition_parameters ---> physical_parameters
     */
    os << '\n' << rhs.get_info_from_derived_object() << '\n';

    /*
     * act_range
     */
    if( !rhs.d_act_range.get_boundary_names().empty() )
    {
        os << '\n' << rhs.d_act_range << '\n';
    }


    return os;
}

} //FreeMAPs
